﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace mp3Transfer
{
    // Константы, используемые в программе
    public class Const
    {
        // Перевод байт в мегабайты
        public static Double B2MB = (Double)1/(1024*1024);
        public static String[] GENRE_LIST = {
                                    "Blues",
                                    "Classic Rock",
                                    "Country",
                                    "Dance",
                                    "Disco",
                                    "Funk",
                                    "Grunge",
                                    "Hip-Hop",
                                    "Jazz",
                                    "Metal",
                                    "New Age",
                                    "Oldies",
                                    "Other",
                                    "Pop",
                                    "R&B",
                                    "Rap",
                                    "Reggae",
                                    "Rock",
                                    "Techno",
                                    "Industrial",
                                    "Alternative",
                                    "Ska",
                                    "Death Metal",
                                    "Pranks",
                                    "Soundtrack",
                                    "Euro-Techno",
                                    "Ambient",
                                    "Trip-Hop",
                                    "Vocal",
                                    "Jazz+Funk",
                                    "Fusion",
                                    "Trance",
                                    "Classical",
                                    "Instrumental",
                                    "Acid",
                                    "House",
                                    "Game",
                                    "Sound Clip",
                                    "Gospel",
                                    "Noise",
                                    "AlternRock",
                                    "Bass",
                                    "Soul",
                                    "Punk",
                                    "Space",
                                    "Meditative",
                                    "Instrumental Pop",
                                    "Instrumental Rock",
                                    "Ethnic",
                                    "Gothic",
                                    "Darkwave",
                                    "Techno-Industrial",
                                    "Electronic",
                                    "Pop-Folk",
                                    "Eurodance",
                                    "Dream",
                                    "Southern Rock",
                                    "Comedy",
                                    "Cult",
                                    "Gangsta",
                                    "Top 40",
                                    "Christian Rap",
                                    "Pop/Funk",
                                    "Jungle",
                                    "Native American",
                                    "Cabaret",
                                    "New Wave",
                                    "Psychadelic",
                                    "Rave",
                                    "Showtunes",
                                    "Trailer",
                                    "Lo-Fi",
                                    "Tribal",
                                    "Acid Punk",
                                    "Acid Jazz",
                                    "Polka",
                                    "Retro",
                                    "Musical",
                                    "Rock & Roll",
                                    "Hard Rock",
                                    "Folk",
                                    "Folk-Rock",
                                    "National Folk",
                                    "Swing",
                                    "Fast Fusion",
                                    "Bebob",
                                    "Latin",
                                    "Revival",
                                    "Celtic",
                                    "Bluegrass",
                                    "Avantgarde",
                                    "Gothic Rock",
                                    "Progressive Rock",
                                    "Psychedelic Rock",
                                    "Symphonic Rock",
                                    "Slow Rock",
                                    "Big Band",
                                    "Chorus",
                                    "Easy Listening",
                                    "Acoustic",
                                    "Humour",
                                    "Speech",
                                    "Chanson",
                                    "Opera",
                                    "Chamber Music",
                                    "Sonata",
                                    "Symphony",
                                    "Booty Bass",
                                    "Primus",
                                    "Porn Groove",
                                    "Satire",
                                    "Slow Jam",
                                    "Club",
                                    "Tango",
                                    "Samba",
                                    "Folklore",
                                    "Ballad",
                                    "Power Ballad",
                                    "Rhythmic Soul",
                                    "Freestyle",
                                    "Duet",
                                    "Punk Rock",
                                    "Drum Solo",
                                    "A capella",
                                    "Euro-House",
                                    "Dance Hall"
                                   };
    }
    
    // Файл [песни?]
    public class Song
    {
        // Стандартный конструктор
        public Song(String _name, Double _size)
        {
            name = _name;
            size = _size;
        }
        // Полный путь к файлу
        String name;
        // Размер файла в мегабайтах
        Double size;
        public String Name
        {
            get
            {
                return name;
            }
        }
        public Double Size
        {
            get
            {
                return size;
            }
        }
    }
    
    // Жанр. Списки жанров - основа программы
    public class Genre
    {
        // Список файлов. public? добавить возможность перебора файлов 
        // без прямого доступа. итератор?
        public List<Song> songs;
        // Номер жанра в массиве Const.GENRE_LIST
        Int16 genre;

        // Конструктор на основе конкретного жанра
        public Genre(Int16 _genre)
        {
            songs = new List<Song>();
            genre = _genre;
        }
        // Получить жанр
        public Int16 GSGenre
        {
            get
            {
                return genre;
            }
        }
        // Количество файлов данного жанра
        public Int64 Count
        {
            get
            {
                return songs.Count;
            }
        }
        // Общий размер списка в мегабайтах
        public Double Size
        {
            get
            {
                Double tmp_size = 0;
                foreach (Song tempSong in songs)
                {
                    tmp_size += tempSong.Size;
                }
                return tmp_size;
            }
        }
        // Добавить новый элемент
        public Int16 AddSong(Song _song)
        {
            if (_song != null && !_song.Name.Equals(""))
            {
                songs.Add(_song);
                return 0;
            }
            return 1;
        }
        // Получить элемент по индексу
        public Song GetSong(Int32 _index)
        {
            if (_index >= 0 && _index < songs.Count)
            {
                return songs[_index];
            }
            return null;
        }
        // Удалить элемент по индексу
        public Int16 DeleteSong(Int32 _index)
        {
            if (_index >= 0 && _index < songs.Count)
            {
                songs.Remove(songs[_index]);
                return 0;
            }
            return 1;
        }
        // Получить и удалить(извлечь) элемент по индексу
        public Song GetNDelSong(Int32 _index)
        {
            Song tempSong = GetSong(_index);
            if (tempSong != null)
            {
                if (DeleteSong(_index) == 0)
                {
                    return tempSong;
                }
            }
            return null;
        }
    }

    // Плейлист
    public class PlaySet
    {
        // Конструктор плейлиста заданного размера
        public PlaySet(Double _maxSize)
        {
            songs = new List<Song>();
            nowSize = 0;
            maxSize = _maxSize;
            averageSize = 0;
            count = 0;
        }
        // Список файлов
        public List<Song> songs;
        public Double nowSize;
        public Double maxSize;
        Double averageSize;
        Int64 count;
        public Int16 AddSong(Song _song)
        {
            if (_song != null && !_song.Name.Equals(""))
            {
                songs.Add(_song);
                nowSize += _song.Size;
                count++;
                return 0;
            }
            return 1;
        }
        public Double AverageSize
        {
            get
            {
                return (Double)nowSize/count;
            }
        }
        public Int64 Count
        {
            get
            {
                return count;
            }
        }
    }
    public class Statistics
    {
        public Statistics()
        {
            averageSize = 0;
            totalSize = 0;
            totalCount = 0;
            totalDirCount = 0;
        }
        public Double totalSize;
        public Double averageSize;
        public Int64 totalCount;
        public Int64 totalDirCount;
    }

    class Program
    {
        public static List<Genre> genres;
        public static Statistics stat;
        public static PlaySet playset;

        static void Main(string[] args)
        {
            try
            {
                Double maxSize = 256;

                genres = new List<Genre>();
                stat = new Statistics();
                playset = new PlaySet(maxSize);

                SearchInDir(@"E:\temp\Avantasia", genres);

                Console.WriteLine("-----------------------------------\n");
                Console.WriteLine("Total songs:  {0}", stat.totalCount);
                Console.WriteLine("Total dirs:   {0}", stat.totalDirCount);
                Console.WriteLine("Total size:   {0}", stat.totalSize.ToString("F02"));
                Console.WriteLine("Average size: {0}", stat.averageSize.ToString("F02"));
                Console.WriteLine();

                List<Double> genreRate = new List<Double>(genres.Count);
                foreach (Genre tempGenre in genres)
                {
                    Console.WriteLine("Жанр {0}, {1} - песен, {2} mb", Const.GENRE_LIST[tempGenre.GSGenre], tempGenre.Count, tempGenre.Size.ToString("F02"));
                }
                
                Console.WriteLine("-------------------------------");
                genreRate.Add(0.70);
                genreRate.Add(0.30);
                SetPlaySet(playset, maxSize, genreRate);
                Console.WriteLine("-------------------------------");

                foreach (Song tempSong in playset.songs)
                {
                    //Console.WriteLine("{0}", tempSong.Name);
                }
                Console.WriteLine("Total songs: {0}\nTotal size: {1}\n", playset.Count, playset.nowSize.ToString("F02"));
            }
            catch (Exception e)
            {
                System.Console.WriteLine("Error: {0}", e.Message);
            }
        }

        public static void SearchInDir(String _pathToDir, List<Genre> _genres)
        {
            try
            {
                String[] directories = Directory.GetDirectories(_pathToDir);

                foreach (String tempDir in directories)
                {
                    SearchInDir(tempDir, _genres);
                }

                String[] filesInDir = Directory.GetFiles(_pathToDir, "*.mp3");
                if (filesInDir.Length != 0) stat.totalDirCount++;
                foreach (String tempFile in filesInDir)
                {
                    Int64 fileLength;

                    FileStream fstream = new FileStream(tempFile, FileMode.Open, FileAccess.ReadWrite);
                    fileLength = fstream.Length;
                    Double fileLengthDbl = (Double)fileLength * Const.B2MB;
                    
                    fstream.Position = fileLength - 128;
                    StreamReader sreader = new StreamReader(fstream);
                    String tags = sreader.ReadToEnd();

                    Int16 tagGenre = 12;
                    //Если есть теги
                    if (tags.Substring(0, 3).Equals("TAG"))
                    {
                        tagGenre = (Int16)tags.Substring(127, 1)[0];
                        //Если жанр в теге почему-то вне диапазона - меняем его на 12 - Other
                        if (tagGenre < 0 || tagGenre > 125)
                        {
                            tagGenre = 12;
                            fstream.Position = fileLength - 1;
                            StreamWriter swriter = new StreamWriter(fstream);
                            swriter.Write((char)12);
                        }
                    }
                    //Если тегов нет
                    else
                    {
                        fstream.Position = fileLength;
                        StreamWriter swriter = new StreamWriter(fstream);
                        swriter.Write("TAG");
                        for (int i = 0; i < 124; i++)
                        {
                            swriter.Write(' ');
                        }
                        swriter.Write((char)12);
                    }

                    Boolean find_genre = false;
                    for (int i = 0; i < _genres.Count; i++)
                    {
                        //Если список для этого жанра есть - добавляем туда песню
                        if (_genres[i].GSGenre == tagGenre)
                        {
                            find_genre = true;
                            if (_genres[i].AddSong(new Song(tempFile, fileLengthDbl)) != 0)
                            {
                                Console.WriteLine("Error adding song");
                            }
                        }
                    }
                    //Если этот жанр встретился впервые - создаём список для него и добавляем туда первую песню
                    if (!find_genre)
                    {
                        _genres.Add(new Genre(tagGenre));
                        if (_genres[_genres.Count - 1].AddSong(new Song(tempFile, fileLengthDbl)) != 0)
                        {
                            Console.WriteLine("Error adding song");
                        }
                    }

                    sreader.Close();
                    fstream.Close();

                    //Console.WriteLine(file + " - " + ((Double)iLength * Const.B2MB).ToString("F02"));
                    stat.totalSize += fileLengthDbl;
                    
                    stat.totalCount++;
                }
            }
            catch (Exception e)
            {
                System.Console.WriteLine("Error in SearchInDir: {0}", e.Message);
            }
        }
        public static void SetPlaySet(PlaySet _playset, Double _maxSize, List<Double> _genreRate)
        {
            try
            {
                _playset.nowSize = 0;
                Random rand = new Random();

                Int32 i = 0;
                foreach (Genre tempGenre in genres)
                {
                    Console.WriteLine("Genre: {0}", Const.GENRE_LIST[tempGenre.GSGenre]);
                    Double tempGSize = 0;
                    while (true)
                    {
                        Int32 tempI = rand.Next(0, tempGenre.songs.Count - 1);
                        tempGSize += tempGenre.songs[tempI].Size;
                        if (tempGSize < (Double)_genreRate[i] * _maxSize)
                        {
                            Song tempSong = new Song(tempGenre.songs[tempI].Name, tempGenre.songs[tempI].Size);
                            _playset.AddSong(tempSong);
                        }
                        else
                        {
                            i++;
                            break;
                        }
                    }
                    //Console.WriteLine("{0} < {1}", tempGSize.ToString("F02"), ((Double)_genreRate[i-1] * iMaxSize).ToString("F02"));
                }
            }
            catch (Exception e)
            {
                System.Console.WriteLine("Error in SetPlaySet: ", e.Message);
            }
        }
    }
}
